<?php
// +-------------------------------------------------------------------
// | 
// +-------------------------------------------------------------------
// | Copyright (c) 2009-2016 All rights reserved.
// +-------------------------------------------------------------------
namespace Kcdns\Service\Util;

/**
 * 格式验证工具
 *
 * @package Service\Util
 *
 * @author Sadaharu#1 <hechengyuan@ucfgroup.com>
 */
class Validate
{

    static $instace;

    public $error;

    public static function getInstance()
    {
        self::$instace or self::$instace = new self();
        return self::$instace;
    }

    /**
     * 输入参数验证/过滤
     *
     * @param unknown $data
     * @param unknown $rules
     * @throws \Exception
     * @return multitype:Ambigous <multitype:> multitype:NULL Ambigous <multitype:>
     */
    public function check($data, $rules = array(), $dataKey = null)
    {
        $returnString = false;
        $this->error = false;

        if (!is_array($data) || !is_array($rules)) {
            $returnString = '_value_';
            is_array($data) or $data = array(
                $returnString => $data
            );
            is_array($rules) or $rules = array(
                $returnString => $rules
            );
        }

        $newData = array();
        $dataKey = $dataKey ?: $returnString;
        $filedDataKey = $dataKey;

        // 以 rules 为主, 同步展开数据
        foreach ($rules as $_rule_k => $_rule_v) {
            // 值为子数组
            list ($_value_k, $_value_array_required) = explode(':', $_rule_k);
            $_value_k=preg_replace('/\[.*\]/','',$_value_k);

            $filedDataKey = $_rule_k === $returnString ? $dataKey : "{$dataKey}[$_value_k]";

            if (is_array($_rule_v)) {
                // 1.规则子元素为索引数组, $rule = [['id'=>'required;int']] 对应 $data = [['id'=>1],['id'=>2],['id'=>3]]
                // 2.规则子元素为索引值, $rule = ['required;int'] 对应 $data = [2, 3,'a'];
                if (is_array($_rule_v[0]) || is_string($_rule_v[0])) {
                    if (is_array($data[$_value_k])) {
                        if (!$data[$_value_k]) {
                            $newData[$_value_k] = [];
                        }

                        foreach ($data[$_value_k] as $_index => $_data_value) {
                            $newData[$_value_k][] = $this->check($_data_value, $_rule_v[0], "{$filedDataKey}[$_index]");
                        }
                    }
                } // 输入数组为索引数组
                elseif ($_rule_k === 0) {
                    if (is_array($data)) {
                        foreach ($data as $_index => $_data_value) {
                            $newData[] = $this->check($_data_value, $_rule_v[0] ?: $_rule_v, "{$filedDataKey}[$_index]");
                        }
                    }
                } else {
                    $newData[$_value_k] = $this->check($data[$_value_k], $_rule_v, "{$filedDataKey}");
                }
            } else {
                $_filed_status = $this->_checkField($data[$_value_k], $_rule_v);
                if ($_filed_status !== true) {
                    list ($_field_label, $_field_rule) = $_filed_status;
                    $this->error = [
                        'key' => $filedDataKey,
                        'label' => $_field_label,
                        'rule' => $_field_rule,
                        'message' => $_field_label ? ($_field_rule == 'required' ? "请填写[$_field_label]" : "[$_field_label]格式错误") : ''
                    ];

                    $fieldLabelMsg = $_field_rule . 'Msg';
                    if ($this->$fieldLabelMsg) {
                        $message = str_replace('__FIELD_LABEL__', $_field_label, $this->$fieldLabelMsg);
                    } else {
                        $message = $_field_label . "格式错误";
                    }

                    throw new \Exception($message);
                }
                isset($data[$_value_k]) and $newData[$_value_k] = $data[$_value_k];
            }
        }
        return $returnString ? $newData[$returnString] : $newData;
    }

    protected function _checkField($value, $rule)
    {
        $parsedRule = $this->_parseRule($rule);
        foreach ($parsedRule as $_method => $_rule) {
            if (!method_exists($this, $_method)) {
                continue;
            }

            // 空值不检测格式
            $_status = true;
            if (($value !== '' && !is_null($value)) || $_method == 'required') {
                $values=is_array($value) ? $value : [$value];
                foreach ($values as $value){
                    $_status = call_user_func(array(
                        $this,
                        $_method
                    ), $value, $_rule);
                }
            }

            if ($_status === false) {
                return [
                    $parsedRule['label'] ?: '',
                    $_method
                ];
            }
        }

        return true;
    }

    protected function _parseRule($rule)
    {
        $rulesArray = explode(';', $rule);
        $parserRule = [];
        foreach ($rulesArray as $_v) {
            if ($_v == '') {
                continue;
            }
            if (!strstr($_v, '=')) {
                $_v .= "=$_v";
            }
            list ($_ruleName, $_ruleValue) = explode('=', $_v);
            $parserRule[$_ruleName] = $_ruleValue;
        }
        return $parserRule;
    }

    // *************************************************************************************
    // *************************************** Rules ***************************************
    // *************************************************************************************
    protected function required($value, $rule)
    {
        $this->{__FUNCTION__ . 'Msg'} = '没有提交__FIELD_LABEL__';
        return isset($value) && (is_string($value) ? trim($value) !== '' : true);
    }

    protected function number($value, $rule)
    {
        return is_numeric($value);
    }

    protected function date($value, $rule)
    {
        $time = strtotime($value);
        return $time ? (date('Y-m-d', $time)) === $value : false;
    }

    protected function datetime($value, $rule)
    {
        $time = strtotime($value);
        return $time ? (date('Y-m-d H:i:s', $time)) === $value : false;
    }

    protected function int($value, $rule)
    {
        return is_numeric($value) ? intval($value) == $value : false;
    }

    protected function mobile($value, $rule)
    {
        return (boolean)preg_match('/^1\d{10}$/', $value);
    }

    protected function email($value, $rule)
    {
        return filter_var($value, FILTER_VALIDATE_EMAIL);
    }

    protected function enum($value, $rule)
    {
        return in_array($value, explode(',', $rule));
    }

    protected function pattern($value, $rule)
    {
        return (boolean)preg_match($rule, $value);
    }

    protected function zipcode($value, $rule)
    {
        return (boolean)preg_match('/^\d+$/', $value);
    }

    protected function length($value, $rule)
    {
        $range = explode(',', $rule);

        if ($range[1]) {
            $msg = '__FIELD_LABEL__长度必须是' . join('-', array_filter($range)) . '位';
        } else {
            $msg = '__FIELD_LABEL__长度必须是' . $range[0] . '位以上';
        }
        $this->{__FUNCTION__ . 'Msg'} = $msg;

        $length = mb_strlen($value, 'utf8');
        if (count($range) == 1) {
            return $length == (int)$rule;
        } else {
            $range[0] = abs((int)$range[0]);
            $range[1] = abs((int)($range[1] ?: 65535));
            return $length <= $range[1] && $length >= $range[0];
        }
    }

    protected function min($value, $rule)
    {
        return $this->number($value, $rule) && $value >= $rule;
    }

    protected function max($value, $rule)
    {
        return $this->number($value, $rule) && $value <= $rule;
    }

    protected function mobile_or_email($value, $rule)
    {
        return $this->mobile($value, $rule) || $this->email($value, $rule);
    }

    protected function bankcard($value, $rule)
    {
        $n = 0;
        for ($i = strlen($value) - 1; $i >= 0; $i--) {
            if ($i % 2) {
                $n += $value{$i};
            } else {
                $t = $value{$i} * 2;
                $t > 9 and ($t = $t{0} + $t{1});
                $n += $t;
            }
        }
        return ($n % 10) == 0;
    }

    protected function idcard($value, $rule)
    {
        $vCity = explode(',', '11,12,13,14,15,21,22,23,31,32,33,34,35,36,37,41,42,43,44,45,46,50,51,52,53,54,61,62,63,64,65,71,81,82,91');

        if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $value))
            return false;

        if (!in_array(substr($value, 0, 2), $vCity))
            return false;

        $value = preg_replace('/[xX]$/i', 'a', $value);
        $vLength = strlen($value);

        if ($vLength == 18) {
            $vBirthday = substr($value, 6, 4) . '-' . substr($value, 10, 2) . '-' . substr($value, 12, 2);
        } else {
            $vBirthday = '19' . substr($value, 6, 2) . '-' . substr($value, 8, 2) . '-' . substr($value, 10, 2);
        }

        if (date('Y-m-d', strtotime($vBirthday)) != $vBirthday)
            return false;

        if ($vLength == 18) {
            $vSum = 0;

            for ($i = 17; $i >= 0; $i--) {
                $vSubStr = substr($value, 17 - $i, 1);
                $vSum += (pow(2, $i) % 11) * (($vSubStr == 'a') ? 10 : intval($vSubStr, 11));
            }

            if ($vSum % 11 != 1)
                return false;
        }

        return true;
    }

    // 域名格式
    protected function domain($value, $rule)
    {
        $patt = "/^(http(s)?:\/\/)?[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9]+)+$/";
        return (boolean)preg_match($patt, $value);
    }

    // 检查 url 是否在当前域名下
    protected function hosturl($value)
    {
        $parts = parse_url($value);
        return $parts['host'] == $_SERVER['HTTP_HOST'];
    }

    // 金额, 最多两位小数
    protected function price($value)
    {
        return is_numeric($value) && $value >= 0 && !preg_match('/\.\d{3,}$/', (string)$value);
    }

    //中英文 姓名
    protected function name($value)
    {
        $preg_name = '/^[\x{4e00}-\x{9fa5}]{2,10}$|^[a-zA-Z\s]*[a-zA-Z\s]{2,20}$/isu';
        return (bool)preg_match($preg_name, $value);
    }

    //地区
    protected function area($value)
    {
        return OE('common')->checkArea($value);
    }

    //开户行
    protected function banktype($value)
    {
        $list = O('common')->getBankList();

        if (!isset($list[$value])) {
            throw new \Exception("无效的开户行");
        }

        return true;
    }
}